﻿using Face8280WebSocketServer.WebSockt;
using DoNetDrive.AspWebSocke;
using DoNetDrive.AspWebSocke.WebSocketClient;
using DoNetDrive.AspWebSocke.WebSocketServer;
using DoNetDrive.Core;
using DoNetDrive.Core.Command;
using DoNetDrive.Core.Command.Text;
using DoNetDrive.Core.Connector;
using System.Collections.Concurrent;
using Face8280WebSocketServer.DTO.FaceRecard;
using Newtonsoft.Json.Serialization;
using DoNetDrive.Core.Connector.UDP;
using Face8280WebSocketServer.Command.Search;
using DoNetDrive.Core.Factory;
using Face8280WebSocketServer.Command.Models;
using Face8280WebSocketServer.Interface;
using DotNetty.Buffers;
using Newtonsoft.Json.Linq;

namespace Face8280WebSocketServer.Command
{
    public static class Allocator
    {
        public static string UDPIP { get; set; }

        public static int DrivePort { get; set; }
        public static int UDPPort { get; set; }
        private static ConnectorAllocator mAllocator { get; set; }
        /// <summary>
        /// 推送消息处理
        /// </summary>
        private static WebSocketIOObserverHandler handler = new WebSocketIOObserverHandler();
        /// <summary>
        /// 服务端列表
        /// </summary>
        public static ConcurrentDictionary<string, WebSocketServerClientDetail> WebSocketServerClients = new();
        /// <summary>
        /// 客户端列表
        /// </summary>
        public static ConcurrentDictionary<string, WebSocketClientDetail> WebSocketClients = new();
        /// <summary>
        /// 设备列表
        /// </summary>
        //public static ConcurrentDictionary<string, DB.Device.FaceDeviceInfo> DeviceList;

        /// <summary>
        /// 设备推送的消息全部接收到此处
        /// </summary>
        public static ConcurrentBag<PushMessageInfo> PushMessagesBag = new ConcurrentBag<PushMessageInfo>();
        static IFaceIOLogService _FaceIOLogService;

        public static void SetLogService(IFaceIOLogService faceIOLogService)
        {
            _FaceIOLogService = faceIOLogService;
        }
        static Allocator()
        {
            // WebSocketClients["2312"].s
            //var sn = "FC-A103M21116000";
            //WebSocketClients.TryAdd(sn, new WebSocketClientDetail(sn, $"ws://192.168.1.130:8601/ws"));
            //替换连接通道工厂
            var factory = ConnectorAllocator.DefaultConnectorFactory as DefaultConnectorFactory;
            factory.ConnectorFactoryDictionary.TryAdd(ConnectorType.WebSocketClient, new WebSocketConnectorFactory());
            mAllocator = ConnectorAllocator.GetAllocator();
            mAllocator.TransactionMessage += MAllocator_TransactionMessage;
            mAllocator.ClientOnline += MAllocator_ClientOnline;
            mAllocator.ClientOffline += MAllocator_ClientOffline;
            handler.OnMessageEvent += Handler_OnMessageEvent;
            handler.OnReceiveEvent += Handler_OnReceiveEvent;
            handler.OnSendEvent += Handler_OnSendEvent;
            mAllocator.CommandCompleteEvent += MAllocator_CommandCompleteEvent;
            mAllocator.CommandTimeout += MAllocator_CommandTimeout;

            mAllocator.CommandProcessEvent += MAllocator_CommandProcessEvent;
            mAllocator.ConnectorConnectedEvent += MAllocator_ConnectorConnectedEvent;
            mAllocator.ConnectorErrorEvent += MAllocator_ConnectorErrorEvent;
            mAllocator.CommandErrorEvent += MAllocator_CommandErrorEvent;
            //  SearchDevice();
        }
        private static void MAllocator_CommandTimeout(object sender, CommandEventArgs e)
        {
            var conndetail = e.ConnectorDetail;
            var cmd = e.Command as JsonCommand;
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "命令超时",
                Body = cmd.Reply + " 耗时：" + (e.CommandDetail.EndTime - e.CommandDetail.BeginTime).TotalMilliseconds,
                IPAddr = GetIPAddr(conndetail),
                FileSize = 0
            });
        }

        private static void MAllocator_CommandProcessEvent(object sender, CommandEventArgs e)
        {
            var conndetail = e.ConnectorDetail;
            var cmd = e.Command as JsonCommand;
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "命令进度",
                Body = $"正在发送:{cmd.Reply},当前进度{e.Command.getProcessStep()}/{e.Command.getProcessMax()}",
                IPAddr = GetIPAddr(conndetail),
                FileSize = 0
            });
        }

        private static void Handler_OnSendEvent(INConnector connector, string message, int fileSize)
        {
            Console.WriteLine("发送数据：" + message);
            var conndetail = connector.GetConnectorDetail();
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "发送数据",
                Body = message,
                IPAddr = GetIPAddr(conndetail),
                FileSize = fileSize
            });
        }

        private static string GetIPAddr(INConnectorDetail detail)
        {
            if (detail is WebSocketClientDetail clientDetail)
            {
                return clientDetail.Uri.Host;
            }
            else
            {
                if (detail is WebSocketServerClientDetail serverDetail)
                {
                    return serverDetail.Host.Host;
                }
            }
            return String.Empty;
        }

        private static void Handler_OnReceiveEvent(INConnector connector, string message, int fileSize)
        {
            Console.WriteLine($"接收数据:{message},附带发送的文件长度:{fileSize}");
            var conndetail = connector.GetConnectorDetail();
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "接收数据",
                Body = message,
                IPAddr = GetIPAddr(conndetail),
                FileSize = fileSize
            });
        }

        private static void MAllocator_CommandErrorEvent(object sender, CommandEventArgs e)
        {
            var conndetail = e.ConnectorDetail;
            var cmd = e.Command as JsonCommand;
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "命令错误",
                Body = $"{cmd.Reply}，耗时：" + (e.CommandDetail.EndTime - e.CommandDetail.BeginTime).TotalMilliseconds,
                IPAddr = GetIPAddr(conndetail),
                FileSize = 0
            });
        }

        private static void MAllocator_ConnectorErrorEvent(object sender, INConnectorDetail connector)
        {
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = connector.GetKey(),
                Method = "连接设备失败",
                Body = connector.GetError().Message,
                IPAddr = GetIPAddr(connector),
                FileSize = 0
            });
        }

        private static void MAllocator_ConnectorConnectedEvent(object sender, INConnectorDetail connector)
        {
            if (connector is WebSocketClientDetail detail1)
            {
                if (!WebSocketClients.ContainsKey(connector.GetKey()))
                {
                    detail1.Connected = true;
                    WebSocketClients.TryAdd(connector.GetKey(), detail1);
                }
                if (sender is INConnector inr)
                {
                    inr.RemoveRequestHandle(typeof(WebSocketIOObserverHandler));
                    inr.AddRequestHandle(handler);
                    inr.OpenForciblyConnect();
                }
                _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
                {
                    DeviceSN = connector.GetKey(),
                    Method = "连接设备成功",
                    Body = connector.GetError()?.Message,
                    IPAddr = GetIPAddr(connector),
                    FileSize = 0
                });
            }
            Console.WriteLine("连接成功" + connector.ToJson());
        }

        private static void MAllocator_CommandCompleteEvent(object sender, CommandEventArgs e)
        {
            var conndetail = e.ConnectorDetail;
            var cmd = e.Command as JsonCommand;
            _FaceIOLogService.AddIOLog(new DB.Log.FaceIOLog
            {
                DeviceSN = conndetail.GetKey(),
                Method = "命令完成",
                Body = $"{cmd.Reply}，耗时：" + (e.CommandDetail.EndTime - e.CommandDetail.BeginTime).TotalMilliseconds,
                IPAddr = GetIPAddr(conndetail),
                FileSize = 0
            });
        }

        public static async Task<INConnector> OpenConnectorAsync(INConnectorDetail detail)
        {
            var conn = await mAllocator.OpenConnectorAsync(detail);
            WebSocketClients.TryAdd(conn.GetKey(), conn.GetConnectorDetail() as WebSocketClientDetail);
            conn.RemoveRequestHandle(typeof(WebSocketIOObserverHandler));
            conn.AddRequestHandle(handler);
            conn.OpenForciblyConnect();
            return conn;
        }
        public static INConnector OpenConnector(INConnectorDetail detail)
        {
            var conn = mAllocator.OpenConnector(detail);
            return conn;
        }
        private static void Handler_OnMessageEvent(INConnector connector, string message, IByteBuffer buf)
        {
            Console.WriteLine("推送消息：" + message);
            byte[] file = null;
            if (buf.ReadableBytes > 0)
            {
                file = new byte[buf.ReadableBytes];
                buf.ReadBytes(file, 0, file.Length);
            }
            PushMessagesBag.Add(new PushMessageInfo
            {
                Message = JObject.Parse(message),
                DeviceSn = connector.GetConnectorDetail().GetKey(),
                File = file
            });
        }

        private static void MAllocator_ClientOffline(object sender, ServerEventArgs e)
        {
            if (sender is INConnector inr)
            {
                var key = inr.GetKey();
                var connectDetail = inr.GetConnectorDetail();
                if (connectDetail is WebSocketServerClientDetail &&
                WebSocketServerClients.ContainsKey(key))
                {
                    WebSocketServerClients.Remove(key, out _);
                }
            }
        }

        private static void MAllocator_ClientOnline(object sender, ServerEventArgs e)
        {
            if (sender is INConnector inr)
            {
                var key = inr.GetKey();
                var first = false;
                var connectDetail = inr.GetConnectorDetail();
                if (connectDetail is WebSocketClientDetail detail1 &&
                    !WebSocketClients.ContainsKey(key))
                {
                    WebSocketClients.TryAdd(key, detail1);
                    first = true;
                }
                else if (connectDetail is WebSocketServerClientDetail detail &&
                 !WebSocketServerClients.ContainsKey(key))
                {
                    WebSocketServerClients.TryAdd(key, detail);
                    first = true;
                }
                if (first == true)
                {
                    //if (DeviceList != null && DeviceList.ContainsKey(key))
                    //{
                    //    DeviceList[key].Status = true;
                    //}
                    inr.RemoveRequestHandle(typeof(WebSocketIOObserverHandler));
                    inr.AddRequestHandle(handler);
                    inr.OpenForciblyConnect();
                }
            }
        }

        public static void AddCommand(INCommand command)
        {
            mAllocator.AddCommand(command);
        }

        public static void AddConnector(string key, INConnector connector)
        {
            mAllocator.AddConnector(key, connector);
        }
        public static INConnector GetConnector(INConnectorDetail detail)
        {
            return mAllocator.GetConnector(detail);
        }
        public static async Task AddCommandAsync(INCommand command)
        {
            await mAllocator.AddCommandAsync(command);
        }
        private static void MAllocator_TransactionMessage(DoNetDrive.Core.Connector.INConnectorDetail connector, DoNetDrive.Core.Data.INData EventData)
        {

        }
        public static async Task<INCommandResult> GetCommandResult(INCommand command)
        {
            await mAllocator.AddCommandAsync(command);
            return command.getResult();
        }

        public static INCommandDetail GetCommandDetail(string sn, string ip, int port, string token = "FFFFFFFF")
        {
            WebSocketClientDetail connDtl = new WebSocketClientDetail(sn, $"ws://{ip}:{port}/WebSocket");
            connDtl.Headers.Add("Token", token);
            var cmdDtl = new TextCommandDetail(connDtl)
            {
                Timeout = 3000
            };
            return cmdDtl;
        }
        public static async Task<INCommandDetail> GetCommandDetail(string sn)
        {
            var connDtl = await GetConnectorDetail(sn);
            var cmdDtl = new TextCommandDetail(connDtl)
            {
                Timeout = 3000
            };
            return cmdDtl;
        }
        public static WebSocketClientDetail GetWebSocketClientDetail(string sn, string ip, int port, string token = "FFFFFFFF")
        {
            WebSocketClientDetail connDtl = new WebSocketClientDetail(sn, $"ws://{ip}:{port}/Webscoket");
            connDtl.Headers.Add("Token", token);
            return connDtl;
        }
        public static async Task<INConnectorDetail> GetConnectorDetail(string sn)
        {
            if (WebSocketClients.ContainsKey(sn))
            {
                var client = WebSocketClients[sn];
                if (!client.Connected)
                {
                  await  OpenConnectorAsync(client);
                }
                return WebSocketClients[sn];
            }
            if (WebSocketServerClients.ContainsKey(sn))
            {
                return WebSocketServerClients[sn];
            }
            return null;
        }
        public static async Task<INCommandDetail> GetCommandDetail(WebSocketClientDetail connDtl)
        {
            //  WebSocketClientDetail connDtl = new WebSocketClientDetail(sn, $"ws://{ip}:{port}/ws");
            if (!connDtl.Connected)
            {
              await  OpenConnectorAsync(connDtl);
            }
            var cmdDtl = new TextCommandDetail(connDtl)
            {
                Timeout = 3000
            };
            return cmdDtl;
        }
        public static INCommandDetail GetCommandDetail(WebSocketServerClientDetail connDtl)
        {
            var cmdDtl = new TextCommandDetail(connDtl)
            {
                Timeout = 3000
            };
            return cmdDtl;
        }

        public static INCommandDetail GetCommandDetail(INConnectorDetail connDtl)
        {
            var cmdDtl = new TextCommandDetail(connDtl)
            {
                Timeout = 3000
            };
            return cmdDtl;
        }
        /// <summary>
        /// 更新客户端信息
        /// </summary>
        /// <param name="oldSn"></param>
        /// <param name="newSn"></param>
        /// <param name="newToken"></param>
        public static void UpdateWebSocketClientDetail(string oldSn, string newSn, string newToken)
        {
            if (string.IsNullOrWhiteSpace(oldSn) && !WebSocketClients.ContainsKey(oldSn))
            {
                return;
            }

            var client = WebSocketClients[oldSn];
            if (!string.IsNullOrWhiteSpace(newToken))
            {
                client.Headers.Clear();
                client.Headers.Add("Token", newToken);
            }
            if (!string.IsNullOrWhiteSpace(newSn))
            {
                client.DeviceSn = newSn;
                if (!oldSn.Equals(newSn))
                {
                    WebSocketClients.Remove(oldSn, out _);
                    WebSocketClients.TryAdd(client.DeviceSn, client);
                }
            }
        }

        public static async Task<List<DeviceSerach>> SearchDevice()
        {
            UDPServerDetail sdetail = new UDPServerDetail(UDPIP, UDPPort);
            UDPSerach cmd = null;
            try
            {

                await mAllocator.OpenConnectorAsync(sdetail);
                var cdetail = SearchCommandDetail();

                cmd = new UDPSerach(cdetail);
                await mAllocator.AddCommandAsync(cmd);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            UDPSerachResult result = null;
            if (cmd != null)
            {
                result = cmd.getResult() as UDPSerachResult;
            }
            await mAllocator.CloseConnectorAsync(sdetail);
            return result?.ResultObj;

        }

        private static INCommandDetail SearchCommandDetail()
        {

            //    UDPClientDetail cdetail1 = new UDPClientDetail()
            string sDestIP = "255.255.255.255";
            //  string sSearchSN = "0000000000000000", sPassword = "FFFFFFFF";
         //   int iDrivePort = DrivePort;

            // string sLocalIP = cmbLocalIP.SelectedItem?.ToString();
            var oUDPDtl = new UDPClientDetail(sDestIP, DrivePort,
                UDPIP, UDPPort);
            var cmdDtl = new TextCommandDetail(oUDPDtl)
            {
                Timeout = 3000
            };
            //var dtl = new OnlineAccessCommandDetail(oUDPDtl, sSearchSN, sPassword);
            //dtl.Timeout = 2000;
            //dtl.RestartCount = 3;
            //dtl.UserData = null;
            return cmdDtl;
        }
    }
}
